﻿#include "CurveAssetCreator.h"
#include "CurveWrappers/FloatCurveInitParam.h"
#include "CurveWrappers/FloatCurveWrapper.h"
#include "Misc/AutomationTest.h"

// UnitTest_CurveWrapper类，用于单元测试。
// 简单起见，暂时不额外添加专用的单元测试Module或Plugin，后续有需求再说。
// 在控制台敲命令 Automation RunTests UnitTest_CurveWrapper 即可运行本单元测试。
IMPLEMENT_SIMPLE_AUTOMATION_TEST(UnitTest_CurveWrapper
                                 , "DefaultNamespace.UnitTest_CurveWrapper"
                                 , EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)

UFloatCurveWrapper* CreateFloatCurveWrapper()
{
	UFloatCurveWrapper* CurveWrapper = NewObject<UFloatCurveWrapper>();
	return CurveWrapper;
}

UCurveFloat* CreateCurveFloat()
{
	UCurveFloat* CurveFloat = NewObject<UCurveFloat>();

	auto KeyHandle = CurveFloat->FloatCurve.UpdateOrAddKey(0.0f, 0.0f);
	CurveFloat->FloatCurve.SetKeyTangentMode(KeyHandle, ERichCurveTangentMode::RCTM_User);

	KeyHandle = CurveFloat->FloatCurve.UpdateOrAddKey(0.25f, -0.25f);
	// 使用RCTM_Auto，看下经过处理后，该模式是否会被保持
	CurveFloat->FloatCurve.SetKeyTangentMode(KeyHandle, ERichCurveTangentMode::RCTM_Auto);

	KeyHandle = CurveFloat->FloatCurve.UpdateOrAddKey(0.5f, 0.5f);
	CurveFloat->FloatCurve.SetKeyTangentMode(KeyHandle, ERichCurveTangentMode::RCTM_User);

	KeyHandle = CurveFloat->FloatCurve.UpdateOrAddKey(0.75f, 1.25f);
	CurveFloat->FloatCurve.SetKeyTangentMode(KeyHandle, ERichCurveTangentMode::RCTM_User);

	KeyHandle = CurveFloat->FloatCurve.UpdateOrAddKey(1.0f, 1.0f);
	CurveFloat->FloatCurve.SetKeyTangentMode(KeyHandle, ERichCurveTangentMode::RCTM_User);

	return CurveFloat;
}

// 使用Unreal Engine的TArray来比较两个浮点数数组
bool AreFloatArraysEqual(const TArray<float>& ArrayLhs, const TArray<float>& ArrayRhs, float Tolerance = 0.0001f)
{
	if (ArrayLhs.Num() != ArrayRhs.Num())
	{
		return false; // 如果两个数组的长度不同，则直接返回false
	}

	for (int i = 0; i < ArrayLhs.Num(); ++i)
	{
		if (fabs(ArrayLhs[i] - ArrayRhs[i]) > Tolerance)
		{
			return false;
		}
	}

	return true;
}

bool Test_InitWithoutChange(UFloatCurveWrapper& CurveWrapper, UCurveFloat& CurveFloat)
{
	FFloatCurveInitParam InitParam;
	InitParam.InitType = FFloatCurveInitParam::EInitType::InitWithoutChange;
	CurveWrapper.Init(&CurveFloat, InitParam);

	// 曲线的值
	TArray<float> AllValue =
	{
		CurveWrapper.GetValueByTime(0), CurveWrapper.GetValueByTime(0.125), CurveWrapper.GetValueByTime(0.25),
		CurveWrapper.GetValueByTime(0.5), CurveWrapper.GetValueByTime(0.75), CurveWrapper.GetValueByTime(1)
	};

	TArray<float> AllExceptValue =
	{
		0.0f, -0.125f, -0.25f, 0.5f, 1.25f, 1.0f
	};

	if (!AreFloatArraysEqual(AllValue, AllExceptValue))
	{
		return false;
	}

	// 归一化的值
	TArray<float> AllNormalizedValue =
	{
		CurveWrapper.GetNormalizedValueByTime(0), CurveWrapper.GetNormalizedValueByTime(0.125),
		CurveWrapper.GetNormalizedValueByTime(0.25), CurveWrapper.GetNormalizedValueByTime(0.5),
		CurveWrapper.GetNormalizedValueByTime(0.75), CurveWrapper.GetNormalizedValueByTime(1)
	};

	TArray<float> AllExceptNormalizedValue =
	{
		0.0f, -0.125f, -0.25f, 0.5f, 1.25f, 1.0f
	};

	if (!AreFloatArraysEqual(AllNormalizedValue, AllExceptNormalizedValue))
	{
		return false;
	}

	return true;
}

bool Test_InitByValueRange(UFloatCurveWrapper& CurveWrapper, UCurveFloat& CurveFloat)
{
	FFloatCurveInitParam InitParam;
	InitParam.InitType = FFloatCurveInitParam::EInitType::InitByValueRange;
	InitParam.ValueRange.Bounds.Min = -2.5;
	InitParam.ValueRange.Bounds.Max = 12.5;
	CurveWrapper.Init(&CurveFloat, InitParam);

	// 曲线的值
	TArray<float> AllValue =
	{
		CurveWrapper.GetValueByTime(0), CurveWrapper.GetValueByTime(0.125), CurveWrapper.GetValueByTime(0.25),
		CurveWrapper.GetValueByTime(0.5), CurveWrapper.GetValueByTime(0.75), CurveWrapper.GetValueByTime(1)
	};

	TArray<float> AllExceptValue =
	{
		0.0f, -1.25f, -2.5f, 5.0f, 12.5f, 10.0f
	};

	if (!AreFloatArraysEqual(AllValue, AllExceptValue))
	{
		return false;
	}

	// 归一化的值
	TArray<float> AllNormalizedValue =
	{
		CurveWrapper.GetNormalizedValueByTime(0), CurveWrapper.GetNormalizedValueByTime(0.125),
		CurveWrapper.GetNormalizedValueByTime(0.25), CurveWrapper.GetNormalizedValueByTime(0.5),
		CurveWrapper.GetNormalizedValueByTime(0.75), CurveWrapper.GetNormalizedValueByTime(1)
	};

	TArray<float> AllExceptNormalizedValue =
	{
		0.0f, -0.125f, -0.25f, 0.5f, 1.25f, 1.0f
	};

	if (!AreFloatArraysEqual(AllNormalizedValue, AllExceptNormalizedValue))
	{
		return false;
	}

	return true;
}

bool Test_InitByStartEndValues_AutoFlipByValue(UFloatCurveWrapper& CurveWrapper, UCurveFloat& CurveFloat)
{
	// 使用本函数独有的关键帧，以便于更精准地进行测试
	CurveFloat.FloatCurve.Reset();
	CurveFloat.FloatCurve.UpdateOrAddKey(0.0f, 0.0f);
	CurveFloat.FloatCurve.UpdateOrAddKey(0.25f, -0.25f);
	CurveFloat.FloatCurve.UpdateOrAddKey(0.5f, 0.5f);
	CurveFloat.FloatCurve.UpdateOrAddKey(0.75f, 1.25f);
	CurveFloat.FloatCurve.UpdateOrAddKey(1.0f, 1.0f);

	FFloatCurveInitParam InitParam;
	InitParam.InitType = FFloatCurveInitParam::EInitType::InitByStartEnd_AutoFlipByValue;
	InitParam.ValueRange.StartAndEnd.Start = 0;
	InitParam.ValueRange.StartAndEnd.End = 10;
	CurveWrapper.Init(&CurveFloat, InitParam);

	// 曲线的值
	TArray<float> AllValue =
	{
		CurveWrapper.GetValueByTime(0), CurveWrapper.GetValueByTime(0.125), CurveWrapper.GetValueByTime(0.25),
		CurveWrapper.GetValueByTime(0.5), CurveWrapper.GetValueByTime(0.75), CurveWrapper.GetValueByTime(1)
	};

	TArray<float> AllExceptValue =
	{
		0.0f, -1.25f, -2.5f, 5.0f, 12.5f, 10.0f
	};

	if (!AreFloatArraysEqual(AllValue, AllExceptValue))
	{
		return false;
	}

	// 归一化的值
	TArray<float> AllNormalizedValue =
	{
		CurveWrapper.GetNormalizedValueByTime(0), CurveWrapper.GetNormalizedValueByTime(0.125),
		CurveWrapper.GetNormalizedValueByTime(0.25), CurveWrapper.GetNormalizedValueByTime(0.5),
		CurveWrapper.GetNormalizedValueByTime(0.75), CurveWrapper.GetNormalizedValueByTime(1)
	};

	TArray<float> AllExceptNormalizedValue =
	{
		0.0f, -0.125f, -0.25f, 0.5f, 1.25f, 1.0f
	};

	if (!AreFloatArraysEqual(AllNormalizedValue, AllExceptNormalizedValue))
	{
		return false;
	}

	return true;
}

bool Test_InitByStartEndValues_AutoFlipByTime(UFloatCurveWrapper& CurveWrapper, UCurveFloat& CurveFloat)
{
	// 根据时间翻转的单元测试：使用本函数独有的关键帧，以便于更精准地进行测试
	CurveFloat.FloatCurve.Reset();
	CurveFloat.FloatCurve.UpdateOrAddKey(0.0f, 0.0f);
	CurveFloat.FloatCurve.UpdateOrAddKey(0.125f, -0.125f);
	CurveFloat.FloatCurve.UpdateOrAddKey(0.25f, -0.25f);
	CurveFloat.FloatCurve.UpdateOrAddKey(0.5f, 0.5f);
	CurveFloat.FloatCurve.UpdateOrAddKey(0.75f, 1.25f);
	CurveFloat.FloatCurve.UpdateOrAddKey(1.0f, 1.0f);

	FFloatCurveInitParam InitParam;
	InitParam.InitType = FFloatCurveInitParam::EInitType::InitByStartEnd_AutoFlipByTime;
	InitParam.ValueRange.StartAndEnd.Start = 10; // 反过来，起始值比终止值更大
	InitParam.ValueRange.StartAndEnd.End = 0;
	CurveWrapper.Init(&CurveFloat, InitParam);

	// 曲线的值
	TArray<float> AllValue;
	AllValue.Add(CurveWrapper.GetValueByTime(0));
	AllValue.Add(CurveWrapper.GetValueByTime(0.125)); // 0.125：并非关键帧的时间戳，而是两个关键帧之间的时刻
	AllValue.Add(CurveWrapper.GetValueByTime(0.25));
	AllValue.Add(CurveWrapper.GetValueByTime(0.5));
	AllValue.Add(CurveWrapper.GetValueByTime(0.75));
	AllValue.Add(CurveWrapper.GetValueByTime(0.875));
	AllValue.Add(CurveWrapper.GetValueByTime(1));

	TArray<float> AllExceptValue;
	AllExceptValue.Add(10.0f);
	AllExceptValue.Add(11.25);
	AllExceptValue.Add(12.5f);
	AllExceptValue.Add(5.0f);
	AllExceptValue.Add(-2.5f);
	AllExceptValue.Add(-1.25f);
	AllExceptValue.Add(0.0f);

	if (!AreFloatArraysEqual(AllValue, AllExceptValue))
	{
		return false;
	}

	// 归一化的值
	TArray<float> AllNormalizedValue;
	AllNormalizedValue.Add(CurveWrapper.GetNormalizedValueByTime(0));
	AllNormalizedValue.Add(CurveWrapper.GetNormalizedValueByTime(0.125)); // 0.125：并非关键帧的时间戳，而是两个关键帧之间的时刻
	AllNormalizedValue.Add(CurveWrapper.GetNormalizedValueByTime(0.25));
	AllNormalizedValue.Add(CurveWrapper.GetNormalizedValueByTime(0.5));
	AllNormalizedValue.Add(CurveWrapper.GetNormalizedValueByTime(0.75));
	AllNormalizedValue.Add(CurveWrapper.GetNormalizedValueByTime(0.875));
	AllNormalizedValue.Add(CurveWrapper.GetNormalizedValueByTime(1));

	TArray<float> AllExceptNormalizedValue;
	AllExceptNormalizedValue.Add(1.0f);
	AllExceptNormalizedValue.Add(1.125);
	AllExceptNormalizedValue.Add(1.25f);
	AllExceptNormalizedValue.Add(0.50f);
	AllExceptNormalizedValue.Add(-0.25f);
	AllExceptNormalizedValue.Add(-0.125f);
	AllExceptNormalizedValue.Add(0.0f);

	if (!AreFloatArraysEqual(AllNormalizedValue, AllExceptNormalizedValue))
	{
		return false;
	}

	return true;
}

bool Test_ChangeTimeRange(UFloatCurveWrapper& CurveWrapper)
{
	auto Curve = CurveWrapper.GetCurveAsset();
	if (!ensureMsgf(Curve != nullptr, TEXT("CurveWrapper.GetCurveAsset() should not be nullptr")))
	{
		return false;
	}

	auto NewStartTime = 50.0f;
	auto NewEndTime = 100.0f;
	CurveWrapper.ChangeTimeRange(Curve, NewStartTime, NewEndTime);

	return Curve->FloatCurve.GetFirstKey().Time == NewStartTime
		&& Curve->FloatCurve.GetLastKey().Time == NewEndTime;
}

bool Test_CreateCurveAssetByJson()
{
	auto JsonFilePath = FPaths::Combine(FPaths::ProjectPluginsDir()
	                                    , TEXT("MotionSubtitles/Content/Curves/AllCurvesInfo.json"));
	JsonFilePath = FPaths::ConvertRelativePathToFull(JsonFilePath);
	return FCurveAssetCreator::CreateAllCurveAssetsFromJsonFile(JsonFilePath);
}

bool UnitTest_CurveWrapper::RunTest(const FString& Parameters)
{
	auto CurveWrapper = CreateFloatCurveWrapper();
	TestTrue("CreateFloatCurveWrapper", CurveWrapper != nullptr);

	auto CurveFloat = CreateCurveFloat();
	TestTrue("CreateCurveFloat", CurveFloat != nullptr);

	TestTrue("Test_InitWithoutChange", Test_InitWithoutChange(*CurveWrapper, *CurveFloat));
	TestTrue("Test_InitByValueRange", Test_InitByValueRange(*CurveWrapper, *CurveFloat));
	TestTrue("Test_InitByStartEndValues_AutoFlipByValue"
	         , Test_InitByStartEndValues_AutoFlipByValue(*CurveWrapper, *CurveFloat));
	TestTrue("Test_InitByStartEndValues_AutoFlipByTime"
	         , Test_InitByStartEndValues_AutoFlipByTime(*CurveWrapper, *CurveFloat));

	TestTrue("Test_ChangeTimeRange", Test_ChangeTimeRange(*CurveWrapper));

	TestTrue("Test_CreateCurveAssetByJson", Test_CreateCurveAssetByJson());

	return true;
}
